#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
// 不产⽣僵⼫进程还有另外⼀种办法
int main()
{
    // 父进程
    signal(SIGCHLD, SIG_IGN); // 父进程
    for (int i = 0; i < 10; i++)
    {
        pid_t id = fork(); // 如果我们有10个子进程呢？？6退出了，4个没退
        if (id == 0)
        {
            sleep(3);
            std::cout << "I am child, exit" << std::endl;
            exit(3);
        }
    }
    while (true) 
    {
        std::cout << "I am fater, exit" << std::endl;
        sleep(1);
    }
    return 0;
}

// 用信号捕捉的方案来回收所有的子进程
void WaitAll(int num) // 将进程等待的工作放到信号捕捉函数里
{
    while (true) //循环是为了考虑多进程的情况
    {
		//pid_t n = waitpid(-1, nullptr, 0); // waitpid默认是阻塞的！
        pid_t n = waitpid(-1, nullptr, WNOHANG); // 有可能多个进程局部退出，所以采用非阻塞轮训！
        if (n == 0)
        {
            break;
        }
        else if (n < 0)
        {
            std::cout << "waitpid error " << std::endl;
            break;
        }
    }
    std::cout << "father get a signal: " << num << std::endl;
}
int main()
{
    // 父进程
    signal(SIGCHLD, WaitAll); // 父进程
    for (int i = 0; i < 10; i++)
    {
        pid_t id = fork(); // 如果我们有10个子进程呢？？6退出了，4个没退
        if (id == 0)
        {
            sleep(3);
            std::cout << "I am child, exit" << std::endl;
            if(i <= 6) exit(3);
            else pause();
        }
    }
    while (true) 
    {
        std::cout << "I am fater, exit" << std::endl;
        sleep(1);
    }
    return 0;
}



// 验证子进程退出会给父进程发送SIGCHLD信号
void Say(int num)
{
	std::cout<<"father get signal: "<<num<<std::endl;
}
int main()
{
    // 父进程
    signal(SIGCHLD, Say); // 父进程
	pid_t id = fork();
	if (id == 0)
	{
		std::cout << "I am child, exit" << std::endl;
		sleep(3);
		exit(3);
	}
   
	waitpid(id,nullptr,0);
    std::cout << "I am father, exit" << std::endl;
    return 0;
}



// volatile使用场景demo
//int flag = 0;
volatile int flag = 0;

void handler(int signu)
{
    std::cout << "更改全局变量, " << flag << "-> 1" << std::endl;
    flag = 1;
}

int main()
{
    signal(2, handler);

    while(!flag);
    std::cout << "process quit normal!" << std::endl;
    return 0;
}



//sigaction的使用demo
void handler(int signum)
{
    std::cout << "hello signal: " << signum << std::endl;
    while(true)
    {
        //不断获取pending表！
        sigset_t pending;
        sigpending(&pending);
        for(int i = 31; i >= 1; i--) // 第二次再发送2号信号pending表对应位置就为1，表示被阻塞住了不会递达
        {
            if(sigismember(&pending, i))
                std::cout << "1";
            else
                std::cout << "0";
        }
        std::cout << std::endl;
        sleep(1);
    }
    exit(0);
}

int main()
{
    struct sigaction act, oact;
    act.sa_handler = handler;
    sigemptyset(&act.sa_mask);
    sigaddset(&act.sa_mask, 3);
    sigaddset(&act.sa_mask, 4);
    act.sa_flags = 0;

    sigaction(SIGINT, &act, &oact); // 对2号信号进行了捕捉, 2,3,4都屏蔽

    while(true)
    {
        std::cout << "hello world: " << getpid() << std::endl;
        sleep(1);
    }
    return 0;
}


//验证core dump
int main()
{
    pid_t id = fork();
    if (id == 0)
    {
        sleep(2);
        printf("hello bit\n");
        printf("hello bit\n");
        printf("hello bit\n");
        printf("hello bit\n");
        printf("hello bit\n");
        int a = 10;
        a /= 0;
        printf("hello bit\n");

        exit(1);
    }
    int status = 0;
    waitpid(id, &status, 0);
    printf("signal: %d, exit code: %d, core dump: %d\n",
           (status & 0x7F), (status >> 8) & 0xFF, (status >> 7) & 0x1);
}


//验证信号保存的话题，整合demo
void PrintPending(sigset_t &pending)
{
    printf("我是一个进程(%d), pending: ", getpid());
    for (int signo = 31; signo >= 1; signo--)
    {
        if (sigismember(&pending, signo))
        {
            std::cout << "1";
        }
        else
        {
            std::cout << "0";
        }
    }
    std::cout << std::endl;
}

void handler(int sig)
{
    std::cout << "#######################" << std::endl;
    std::cout << "递达" << sig << "信号!" << std::endl;
    sigset_t pending;
    int m = sigpending(&pending);
    PrintPending(pending); // 0000 0010(处理完，2号才会被设置为0)，0000 0000(执行handler方法之前，2对应的pending已经被清理了)
	//结论：当我们准备递达的时候，要首先清空pending信号集中对应的信号位图，由1变成0
    std::cout << "#######################" << std::endl;
}

int main()
{
    signal(SIGINT, handler);
    // 1. 屏蔽2号信号
    sigset_t block, oblock;
    sigemptyset(&block);
    sigemptyset(&oblock);

    sigaddset(&block, SIGINT); // 已经对2号信号进行屏蔽了吗？没有！
	
	// 9号信号不可被阻塞，9号信号叫做管理员信号
    // for(int i = 1; i<32; i++)
    //     sigaddset(&block, i);

    int n = sigprocmask(SIG_SETMASK, &block, &oblock);
    (void)n;

    // 4. 重复获取打印过程
    int cnt = 0;
    while (true)
    {
        // 2. 获取pending信号集合
        sigset_t pending;
        int m = sigpending(&pending);

        // 3. 打印
        PrintPending(pending);
        if (cnt == 10)
        {
            // 5. 恢复对2号信号的block情况
            std::cout << "解除对2号的屏蔽" << std::endl;
            sigprocmask(SIG_SETMASK, &oblock, nullptr);
        }

        sleep(1);
        cnt++;
    }

    return 0;
}


//理解SIG_DFL和SIG_IGN
void handler(int sig)
{
    std::cout << "hello sig: " << sig << std::endl;
    signal(2, SIG_DFL); // 2 默认动作是终止
    std::cout << "恢复处理动作" << std::endl;
}

int main()
{
    signal(2, handler); // 自定义捕捉
    //signal(2, SIG_IGN); //忽略信号

    while(true)
    {
        sleep(1);
        std::cout << "." << std::endl;
    }
    return 0;
}


//设置重复闹钟：基于闹钟每秒钟让我们的程序醒来一次做相应的动作
//////////func////////////
void Sched()
{
    std::cout << "我是进程调度" << std::endl;
}
void MemManger()
{
    std::cout << "我是周期性的内存管理，正在检查有没有内存问题" << std::endl;
}
void Fflush()
{
    std::cout << "我是刷新程序，我在定期刷新内存数据，到磁盘" << std::endl;
}
/////////////////////////

using func_t = std::function<void()>;
std::vector<func_t> funcs;

// 每隔一秒，完成一些任务
void handlerSig(int sig)
{
    std::cout << "##############################" << std::endl;
    for(auto f : funcs)
        f();
    std::cout << "##############################" << std::endl;
    int n = alarm(1);
}
int main()
{
	funcs.push_back(Sched);
    funcs.push_back(MemManger);
    funcs.push_back(Fflush)；
    signal(SIGALRM, handlerSig);
    alarm(1);
    while(true)
    {
        // 我想让进程什么都不做，就想让进程暂停
        // 一旦来一个信号，就唤醒一次执行方法
        pause();
    }
    return 0;
}


//验证alarm作用，体会IO效率问题
int cnt=0;
void handlerSig(int sig)
{
    std::cout << "获得了一个信号: " << sig <<"cnt: "<<cnt<< std::endl;
    exit(13);
}
int main()
{
    for (int i = 1; i < 32; i++)
        signal(i, handlerSig);

    alarm(1); // 设定1S闹钟，1s以后，当前进程会收到一个信号

    while (true)
    {
		// cout本质是IO，xshell->./XXX->云服务器->网络->我们看到
        // std::cout << "count: " << cnt++ << std::endl; // IO 多，打印效率不高！
		
		cnt++ // IO 少
    }
}


//硬件异常产⽣信号
void handlerSig(int sig)
{
    std::cout << "获得了一个信号: " << sig << std::endl;
    exit(13);
}
int main()
{
    for(int i = 1; i < 32; i++)
        signal(i, handlerSig);
    int cnt = 0;
    while (true)
    {
        std::cout << "hello world, " << cnt++ << " ,pid: " << getpid() << std::endl;
        sleep(1);
        int a = 10;
        // a /= 0; // 除0错误
		
        int *p = nullptr;
        *p = 100; // 野指针
    }
	return 0;
}


//测试abort
void handlerSig(int sig)
{
    std::cout << "获得了一个信号: " << sig << std::endl;
}
int main()
{
    for(int i = 1; i < 32; i++)
        signal(i, handlerSig);
    int cnt = 0;
    while (true)
    {
        std::cout << "hello world, " << cnt++ << " ,pid: " << getpid() << std::endl;
		abort();
        sleep(1);
    }
	return 0;
}


//测试raise
void handlerSig(int sig)
{
    std::cout << "获得了一个信号: " << sig << std::endl;
}
int main()
{
    for(int i = 1; i < 32; i++)
        signal(i, handlerSig);
	for(int i = 1; i < 32; i++)
    {
        sleep(1);
        //if(i == 9 || i == 19)
			//continue; //9号信号和19号信号无法被自定义捕捉！
        raise(i);
    }
    int cnt = 0;
    while (true)
    {
        std::cout << "hello world, " << cnt++ << " ,pid: " << getpid() << std::endl;
        sleep(1);
    }
	return 0;
}


//将所有的普通信号自定义捕捉
void handlerSig(int sig)
{
    std::cout << "获得了一个信号: " << sig << std::endl;
    //exit(13);
}

int main()
{
    for(int i = 1; i < 32; i++)
        signal(i, handlerSig);
    int cnt = 0;
    while (true)
    {
        std::cout << "hello world, " << cnt++ << " ,pid: " << getpid() << std::endl;
        sleep(1);
    }
	return 0;
}



//测试mykill
void handlerSig(int sig)
{
    std::cout << "获得了一个信号: " << sig << std::endl;
}
int main()
{
    //signal(2,handlerSig); // 这个函数只需要调一次设置给进程就可以了，不用循环signal
    signal(SIGINT,handlerSig); 
    int cnt = 0;
    while (true)
    {
        std::cout << "hello world, " << cnt++ <<" ,pid: "<<getpid() << std::endl;
        sleep(1);
    }
	return 0;
}


//使用系统调用 signal 更改进程的默认信号处理动作
void handlerSig(int sig)
{
    std::cout << "获得了一个信号: " << sig << std::endl;
}
int main()
{
    //signal(2,handlerSig); // 这个函数只需要调一次设置给进程就可以了，不用循环signal
    signal(SIGINT,handlerSig); 
    int cnt = 0;
    while (true)
    {
        std::cout << "hello world, " << cnt++  << std::endl;
        sleep(1);
    }
	return 0;
}


//键盘产生信号
int main()
{
    int cnt = 0;
    while (true)
    {
        std::cout << "hello world, " << cnt++ << std::endl;
        sleep(1);
    }
	return 0;
}
